//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "SimilarityTMAlign.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <fstream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 5000
#define TMALIGN    "extern/TMalign"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
SimilarityTMAlign::SimilarityTMAlign() {

  this->filenameList    = new String();
  this->locationDB      = new String();
  this->filenameOut     = new String();
  this->locationOut     = new String();
  this->locationTMAlign = new String();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
SimilarityTMAlign::SimilarityTMAlign(const SimilarityTMAlign& similarityTMAlign) {

  this->filenameList    = new String(similarityTMAlign.filenameList);
  this->locationDB      = new String(similarityTMAlign.locationDB);
  this->filenameOut     = new String(similarityTMAlign.filenameOut);
  this->locationOut     = new String(similarityTMAlign.locationOut);
  this->locationTMAlign = new String(similarityTMAlign.locationTMAlign);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
SimilarityTMAlign::SimilarityTMAlign(const SimilarityTMAlign* similarityTMAlign) {

  this->filenameList    = new String(similarityTMAlign->filenameList);
  this->locationDB      = new String(similarityTMAlign->locationDB);
  this->filenameOut     = new String(similarityTMAlign->filenameOut);
  this->locationOut     = new String(similarityTMAlign->locationOut);
  this->locationTMAlign = new String(similarityTMAlign->locationTMAlign);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
SimilarityTMAlign::~SimilarityTMAlign() {

  if (this->filenameList)    delete this->filenameList;
  if (this->locationDB)      delete this->locationDB;
  if (this->filenameOut)     delete this->filenameOut;
  if (this->locationOut)     delete this->locationOut;
  if (this->locationTMAlign) delete this->locationTMAlign;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void SimilarityTMAlign::FilenameList(String* filenameList) {
  if (filenameList) {
    if (this->filenameList) delete this->filenameList;
    this->filenameList = filenameList;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void SimilarityTMAlign::LocationDB(String* locationDB) {
  if (locationDB) {
    if (this->locationDB) delete this->locationDB;
    this->locationDB = locationDB;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void SimilarityTMAlign::FilenameOut(String* filenameOut) {
  if (filenameOut) {
    if (this->filenameOut) delete this->filenameOut;
    this->filenameOut = filenameOut;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void SimilarityTMAlign::LocationOut(String* locationOut) {
  if (locationOut) {
    if (this->locationOut) delete this->locationOut;
    this->locationOut = locationOut;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void SimilarityTMAlign::LocationTMAlign(String* locationTMAlign) {
  if (locationTMAlign) {
    if (this->locationTMAlign) delete this->locationTMAlign;
    this->locationTMAlign = locationTMAlign;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* SimilarityTMAlign::FilenameList(void) {
  return this->filenameList;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* SimilarityTMAlign::LocationDB(void) {
  return this->locationDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* SimilarityTMAlign::FilenameOut(void) {
  return this->filenameOut;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* SimilarityTMAlign::LocationOut(void) {
  return this->locationOut;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* SimilarityTMAlign::LocationTMAlign(void) {
  return this->locationTMAlign;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool SimilarityTMAlign::GetSimilarityData(void) {

  if (!this->filenameList    || !this->filenameList->Length() ||
      !this->locationDB      || !this->locationDB->Length()   ||
      !this->filenameOut     || !this->filenameOut->Length()  ||
      !this->locationOut     || !this->locationOut->Length()  ||
      !this->locationTMAlign || !this->locationTMAlign->Length())
   return false;

  TListE <String>* listPDB  = new TListE <String> ();

  fstream          fileLIST,    fileLengthT,   fileLengthQ;
  fstream          fileLengthA, fileCoverageT, fileCoverageQ;
  fstream          fileRMSD,    fileTMScore,   fileIdentity, fileLog;

  char             completePathTarget[LENGTHLINE];
  char             completePathQuery [LENGTHLINE];
  char             completePathOut   [LENGTHLINE];
  char             commad            [2 * LENGTHLINE];
  char             buffer            [LENGTHLINE + 1];

  time_t           initialTime;

  initialTime = time(NULL);
  
  fileLIST.open(this->filenameList->Out(), fstream::in);
  while (!fileLIST.eof()) {
    fileLIST.getline(buffer, LENGTHLINE);
    String* pdb = new String(buffer);
    pdb->Trim();
    if (pdb->Length()) listPDB->Add(pdb);
  }
  fileLIST.close();

  sprintf(commad, "%s%sLengthTarget.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileLengthT.open(commad, fstream::out);

  sprintf(commad, "%s%sLengthQuery.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileLengthQ.open(commad, fstream::out);

  sprintf(commad, "%s%sLengthAligned.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileLengthA.open(commad, fstream::out);

  sprintf(commad, "%s%sCoverageTarget.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileCoverageT.open(commad, fstream::out);

  sprintf(commad, "%s%sCoverageQuery.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileCoverageQ.open(commad, fstream::out);

  sprintf(commad, "%s%sRMSD.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileRMSD.open(commad, fstream::out);

  sprintf(commad, "%s%sTMScore.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileTMScore.open(commad, fstream::out);

  sprintf(commad, "%s%sIdentity.matrix\0", this->locationOut->Out(), this->filenameOut->Out());
  fileIdentity.open(commad, fstream::out);

  sprintf(commad, "%s%s.log\0", this->locationOut->Out(), this->filenameOut->Out());
  fileLog.open(commad, fstream::out);

  int**    lengthTarget   = new int*[listPDB->Length()];
  int**    lengthQuery    = new int*[listPDB->Length()];
  int**    lengthAligned  = new int*[listPDB->Length()];
  
  double** rmsd           = new double*[listPDB->Length()];
  double** score          = new double*[listPDB->Length()];
  double** identity       = new double*[listPDB->Length()];
  double** coverageTarget = new double*[listPDB->Length()];
  double** coverageQuery  = new double*[listPDB->Length()];
   
  cout << "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  ShowTime(initialTime);
  cout.width(14); cout.left; cout << "     Target    |";
  cout.width(14); cout.left; cout << "     Query     |";
  cout.width(12); cout.left; cout << "  Length T  |";
  cout.width(12); cout.left; cout << "  Length Q  |";
  cout.width(12); cout.left; cout << "  Length A  |";
  cout.width(8);  cout.left; cout << "    RMSD |";
  cout.width(8);  cout.left; cout << "   Score |";
  cout.width(10); cout.left; cout << "  Identity |";
  cout.width(13); cout.left; cout << " Coverage T |";
  cout.width(13); cout.left; cout << " Coverage Q |";
  cout << endl;
  cout << "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  
  fileLog << "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  fileLog << ShowTime(initialTime, buffer);
  fileLog.width(14); fileLog.left; fileLog << "     Target    |";
  fileLog.width(14); fileLog.left; fileLog << "     Query     |";
  fileLog.width(12); fileLog.left; fileLog << "  Length T  |";
  fileLog.width(12); fileLog.left; fileLog << "  Length Q  |";
  fileLog.width(12); fileLog.left; fileLog << "  Length A  |";
  fileLog.width(8);  fileLog.left; fileLog << "    RMSD |";
  fileLog.width(8);  fileLog.left; fileLog << "   Score |";
  fileLog.width(10); fileLog.left; fileLog << "  Identity |";
  fileLog.width(13); fileLog.left; fileLog << " Coverage T |";
  fileLog.width(13); fileLog.left; fileLog << " Coverage Q |";
  fileLog << endl;
  fileLog << "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" << endl;

  for (int i = 0; i < listPDB->Length(); i++) {

    lengthTarget[i]   = new int[listPDB->Length()];
    lengthQuery[i]    = new int[listPDB->Length()];
    lengthAligned[i]  = new int[listPDB->Length()];

    rmsd[i]           = new double[listPDB->Length()];
    score[i]          = new double[listPDB->Length()];
    identity[i]       = new double[listPDB->Length()];
    coverageTarget[i] = new double[listPDB->Length()];
    coverageQuery[i]  = new double[listPDB->Length()];
    
  }

  for (int i = 0; i < listPDB->Length(); i++) {

    sprintf(completePathTarget, "%s%s\0", this->locationDB->Out(), listPDB->Get(i)->Out());
    
    for (int j = i; j < listPDB->Length(); j++) {

      if (!i) {
        fileLengthT   << listPDB->Get(j)->Out() << " ";
        fileLengthQ   << listPDB->Get(j)->Out() << " ";
        fileLengthA   << listPDB->Get(j)->Out() << " ";
        fileRMSD      << listPDB->Get(j)->Out() << " ";
        fileTMScore   << listPDB->Get(j)->Out() << " ";
        fileIdentity  << listPDB->Get(j)->Out() << " ";
        fileCoverageT << listPDB->Get(j)->Out() << " ";
        fileCoverageQ << listPDB->Get(j)->Out() << " ";
      }

      sprintf(completePathQuery, "%s%s\0",     this->locationDB->Out(),  listPDB->Get(j)->Out());
      sprintf(completePathOut,   "%s%s.tmp\0", this->locationOut->Out(), this->filenameOut->Out());
      
      sprintf(commad, "%s %s %s > %s\0", this->locationTMAlign->Out(), completePathTarget, completePathQuery, completePathOut);
      system(commad);

      this->ReadOutTMAlign(completePathOut, lengthTarget[i][j], lengthQuery[i][j], lengthAligned[i][j], rmsd[i][j], score[i][j], identity[i][j]);

      lengthTarget[j][i]   = lengthTarget[i][j];
      lengthQuery[j][i]    = lengthQuery[i][j];
      lengthAligned[j][i]  = lengthAligned[i][j];
      rmsd[j][i]           = rmsd[i][j];
      score[j][i]          = score[i][j];
      identity[j][i]       = identity[i][j];
      coverageTarget[i][j] = coverageTarget[j][i] = (lengthTarget[i][j])?((double)((double)(lengthAligned[i][j]*100)/(double)lengthTarget[i][j])):0;
      coverageQuery[i][j]  = coverageQuery[j][i]  = (lengthQuery[i][j])?((double)((double)(lengthAligned[i][j]*100)/(double)lengthQuery[i][j])):0;

      ShowTime(initialTime);
      cout.width(14); cout.left; cout << listPDB->Get(i)->Out() << " |";
      cout.width(14); cout.left; cout << listPDB->Get(j)->Out() << " |";
      cout.width(12); cout.left; cout << lengthTarget[i][j] << " |";
      cout.width(12); cout.left; cout << lengthQuery[i][j] << " |";
      cout.width(12); cout.left; cout << lengthAligned[i][j] << " |";
      cout.width(8);  cout.left; cout << rmsd[i][j] << " |";
      cout.width(8);  cout.left; cout << score[i][j] << " |";
      cout.width(10); cout.left; cout << identity[i][j] << " |";
      cout.width(13); cout.left; cout << coverageTarget[i][j] << " |";
      cout.width(13); cout.left; cout << coverageQuery[i][j] << " |";
      cout << endl;
      
      fileLog << ShowTime(initialTime, buffer);
      fileLog.width(14); fileLog.left; fileLog << listPDB->Get(i)->Out() << " |";
      fileLog.width(14); fileLog.left; fileLog << listPDB->Get(j)->Out() << " |";
      fileLog.width(12); fileLog.left; fileLog << lengthTarget[i][j] << " |";
      fileLog.width(12); fileLog.left; fileLog << lengthQuery[i][j] << " |";
      fileLog.width(12); fileLog.left; fileLog << lengthAligned[i][j] << " |";
      fileLog.width(8);  fileLog.left; fileLog << rmsd[i][j] << " |";
      fileLog.width(8);  fileLog.left; fileLog << score[i][j] << " |";
      fileLog.width(10); fileLog.left; fileLog << identity[i][j] << " |";
      fileLog.width(13); fileLog.left; fileLog << coverageTarget[i][j] << " |";
      fileLog.width(13); fileLog.left; fileLog << coverageQuery[i][j] << " |";
      fileLog << endl;

      sprintf(commad, "rm %s\0", completePathOut);
      system(commad);

    }

    if (!i) {
      fileLengthT   << endl;
      fileLengthQ   << endl;
      fileLengthA   << endl;
      fileRMSD      << endl;
      fileTMScore   << endl;
      fileIdentity  << endl;
      fileCoverageT << endl;
      fileCoverageQ << endl;
    }

  }

  for (int i = 0; i < listPDB->Length(); i++) {

    for (int j = 0; j < listPDB->Length(); j++) {

      fileLengthT.width(12);   fileLengthT.left;   fileLengthT   << lengthTarget[i][j]   << " ";
      fileLengthQ.width(12);   fileLengthQ.left;   fileLengthQ   << lengthQuery[i][j]    << " ";
      fileLengthA.width(12);   fileLengthA.left;   fileLengthA   << lengthAligned[i][j]  << " ";
      fileRMSD.width(8);       fileRMSD.left;      fileRMSD      << rmsd[i][j]           << " ";
      fileTMScore.width(8);    fileTMScore.left;   fileTMScore   << score[i][j]          << " ";
      fileIdentity.width(10);  fileIdentity.left;  fileIdentity  << identity[i][j]       << " ";
      fileCoverageT.width(13); fileCoverageT.left; fileCoverageT << coverageTarget[i][j] << " ";
      fileCoverageQ.width(13); fileCoverageQ.left; fileCoverageQ << coverageQuery[i][j]  << " ";
        
    }
      
    fileLengthT   << endl;
    fileLengthQ   << endl;
    fileLengthA   << endl;
    fileRMSD      << endl;
    fileTMScore   << endl;
    fileIdentity  << endl;
    fileCoverageT << endl;
    fileCoverageQ << endl;
      
    if (lengthTarget[i])   delete[] lengthTarget[i];
    if (lengthQuery[i])    delete[] lengthQuery[i];
    if (lengthAligned[i])  delete[] lengthAligned[i];

    if (rmsd[i])           delete[] rmsd[i];
    if (score[i])          delete[] score[i];
    if (identity[i])       delete[] identity[i];
    if (coverageTarget[i]) delete[] coverageTarget[i];
    if (coverageQuery[i])  delete[] coverageQuery[i];

  }

  if (lengthTarget)   delete[] lengthTarget;
  if (lengthQuery)    delete[] lengthQuery;
  if (lengthAligned)  delete[] lengthAligned;

  if (rmsd)           delete[] rmsd;
  if (score)          delete[] score;
  if (identity)       delete[] identity;
  if (coverageTarget) delete[] coverageTarget;
  if (coverageQuery)  delete[] coverageQuery;
  
  fileLengthT.close();
  fileLengthQ.close();
  fileLengthA.close();
  fileCoverageT.close();
  fileCoverageQ.close();
  fileRMSD.close();
  fileTMScore.close();
  fileIdentity.close();
  fileLog.close();

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool SimilarityTMAlign::SplitColumn(char* buffer, TListE<String>* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool SimilarityTMAlign::ReadOutTMAlign(char* filename, int& lengthTarget, int& lengthQuery, int& lengthAligned, double& rmsd, double& score, double& identity) {

  lengthQuery = lengthTarget = lengthAligned = 0;
  rmsd        = score        = identity      = 0;

  if (!filename) return false;

  fstream          fileResult;
  char             buffer1[LENGTHLINE + 1], *buffer2 = NULL;
  TListE <String>* column = new TListE <String> ();

  fileResult.open(filename, fstream::in);
  if (!fileResult.is_open()) return false;

  while (!fileResult.eof()) {

    fileResult.getline(buffer1, LENGTHLINE);

    if (strstr(buffer1, "Floating point exception")) {
      lengthTarget  = lengthQuery = lengthAligned = rmsd = score = identity = 0;
      break;
    }

    if (strstr(buffer1, "Chain 1:")) {
      buffer2 = buffer1;
      column->Clear();
      this->SplitColumn(buffer2, column, " =");
      lengthTarget = atoi(column->Get(3)->Out());
      continue;
    }
    
    if (strstr(buffer1, "Chain 2:")) {
      buffer2 = buffer1;
      column->Clear();
      this->SplitColumn(buffer2, column, " =");
      lengthQuery = atoi(column->Get(3)->Out());
      continue;
    }

    if (strstr(buffer1, "Aligned length")) {
      buffer2 = buffer1;
      column->Clear();
      this->SplitColumn(buffer2, column, " =,");
      lengthAligned = atoi(column->Get(2)->Out());
      rmsd          = atof(column->Get(4)->Out());
      score         = atof(column->Get(6)->Out());
      identity      = atof(column->Get(8)->Out());
      continue;
    }
    
  }

  fileResult.close();

  if (column) delete column;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void SimilarityTMAlign::ShowTime(time_t initialTime) {

    time_t endTime;
    time(&endTime);

    int timeSeconds = (int)difftime(endTime, initialTime);
    int timeMinutes = timeSeconds/60;
    int timeHours   = timeMinutes/60;
    int timeDays    = timeHours/24;

    timeSeconds    %= 60;
    timeMinutes    %= 60;
    timeHours      %= 24;

    printf("Elapsed Time: %02d:%02d:%02d:%02d (DD:HH:MM:SS) |", timeDays, timeHours, timeMinutes, timeSeconds);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* SimilarityTMAlign::ShowTime(time_t initialTime, char* buffer) {

    time_t endTime;
    time(&endTime);

    int timeSeconds = (int)difftime(endTime, initialTime);
    int timeMinutes = timeSeconds/60;
    int timeHours   = timeMinutes/60;
    int timeDays    = timeHours/24;

    timeSeconds    %= 60;
    timeMinutes    %= 60;
    timeHours      %= 24;

    sprintf(buffer, "Elapsed Time: %02d:%02d:%02d:%02d (DD:HH:MM:SS) |\0", timeDays, timeHours, timeMinutes, timeSeconds);

    return buffer;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
